import cv2
from datetime import datetime
import zipfile
from werkzeug.utils import secure_filename
from flask import Flask, request, jsonify
import os
import random
import numpy as np
import shutil

basedir = os.path.abspath(os.path.dirname(__file__))
classifier = cv2.CascadeClassifier(os.path.join(basedir, "haarcascade_frontalface_default.xml"))
tempPath = os.path.join(basedir, "temp")
resultPath = "/data/images/sdk-storage/photoimg/facedetection_test/"
resultPath_visit = "/images/sdk-storage/photoimg/facedetection_test/"


class Faces():
    def __init__(self, leftUperX, leftUperY, rightDownX, rightDownY, centerLeftX, centerLeftY, centerRightX,
                 centerRightY, radiusMin, height, width):
        self.leftUperX = leftUperX
        self.leftUperY = leftUperY
        self.rightDownX = rightDownX
        self.rightDownY = rightDownY
        self.centerLeftX = centerLeftX
        self.centerLeftY = centerLeftY
        self.centerRightX = centerRightX
        self.centerRightY = centerRightY
        self.radiusMin = radiusMin
        self.height = height
        self.width = width

    def isCenterPointInRect(self):
        if not self.centerLeftX > self.leftUperX and self.centerLeftY > self.leftUperY:
            return False
        if not self.centerLeftX > self.leftUperX and self.centerLeftY < self.rightDowny:
            return False
        if not self.centerRightX < self.rightDownX and self.centerRightY > self.rightDowny:
            return False
        if not self.centerRightX < self.rightDownX and self.centerRightY < self.rightDowny:
            return False
        return True


app = Flask(__name__)


@app.route("/upload", methods=["GET", "POST"])
def upload():
    f = request.files.get('file')
    filename = secure_filename(f.filename)
    fileType = fileDetermineType(filename)
    if fileType == 1:
        random_num = random.randint(0, 100)
        filename = datetime.now().strftime("%Y%m%d%H%M%S") + "_" + str(random_num) + "." + filename.rsplit('.', 1)[1]
        img = f.read()
        img1 = cv2.imdecode(np.frombuffer(img, np.uint8), cv2.IMREAD_COLOR)
        faceDetection(img1, filename)
        my_host = "http://106.225.146.202:8090"
        my_host_visit = "http://106.225.146.202:8050"
        new_path_file = my_host_visit + resultPath_visit + filename
        data = {"msg": "success", "code": "200", "url": new_path_file}
        payload = jsonify(data)
        return payload, 200
    elif fileType == 2:
        file_path = tempPath + filename
        f.save(file_path)
        target_path = tempPath
        unzip_file(file_path, target_path)
        os.remove(file_path)
        fileList = os.listdir(tempPath)
        fileListResult = []
        my_host = "http://106.225.146.202:8090"
        my_host_visit = "http://106.225.146.202:8050"
        for item in fileList:
            itemPath = os.path.join(tempPath, item)
            imgBinTransfer = cv2.imread(itemPath)
            random_num = random.randint(0, 100)
            fileNameBinTransfer = datetime.now().strftime("%Y%m%d%H%M%S") + "_" + str(random_num) + "." + \
                                  itemPath.rsplit('.', 1)[1]
            faceDetection(imgBinTransfer, fileNameBinTransfer)
            new_path_file = my_host_visit + resultPath_visit + fileNameBinTransfer
            fileListResult.append(new_path_file)
        shutil.rmtree(target_path)
        os.mkdir(target_path)
        datas = []
        for index in range(len(fileListResult)):
            mydict = {}
            mydict["msg"] = "success"
            mydict["code"] = "200"
            mydict["url"] = fileListResult[index]
            datas.append(mydict)
        payload = jsonify(datas)
        return payload, 200


def faceDetection(imgOrigin, filename):
    print(filename)
    [imgOriginHeight, imgOriginWidth, imgOriginPixels] = imgOrigin.shape
    if imgOriginHeight < 200 or imgOriginWidth < 200:
        return False
    imgGray = cv2.cvtColor(imgOrigin, cv2.COLOR_BGR2GRAY)
    faceRects = classifier.detectMultiScale(imgGray, scaleFactor=1.2, minNeighbors=3, minSize=(32, 32))
    if len(faceRects):
        for faceRect in faceRects:
            x, y, w, h = faceRect
            leftUperX = x
            leftUperY = y
            height = h
            width = w
            rightDownX = x + h
            rightDownY = y + w
            centerLeftX = x + w // 4
            centerLeftY = y + h // 4 + 30
            centerRightX = x + 3 * w // 4
            centerRightY = y + h // 4 + 30
            radiusMin = min(w // 8, h // 8)
            faces = Faces(leftUperX, leftUperY, rightDownX, rightDownY, centerLeftX, centerLeftY, centerRightX,
                          centerRightY, radiusMin, height, width)
            cv2.rectangle(imgGray, (faces.leftUperX, faces.leftUperY), (faces.rightDownX, faces.rightDownY),
                          (255, 0, 0),
                          2)
            cv2.circle(imgGray, (faces.centerLeftX, faces.centerLeftY), faces.radiusMin, (255, 0, 0))
            cv2.circle(imgGray, (faces.centerRightX, faces.centerRightY), faces.radiusMin, (255, 0, 0))
            if not faces.isCenterPointInRect():
                continue
            imgClippingOutput = faceClipping(imgOrigin, faces.leftUperX, faces.leftUperY, faces.rightDownX,
                                             faces.rightDownY, faces.width,
                                             faces.height, imgOriginWidth, imgOriginHeight)
    cv2.imwrite(resultPath + filename, imgClippingOutput)


def faceClipping(imgInput, leftUperXBefore, leftUperYBefore, rightDownXBefore, rightDownYBefore, widthBefore,
                 heightBefore, imgOriginWidth, imgOriginHeight):
    widthAfter = round(widthBefore * 1.5)
    heightAfter = round(widthAfter * (heightBefore / widthBefore) * 1.2)
    leftUperXOffeset = round((heightAfter - heightBefore) / 2)
    leftUperYOffeset = round((widthAfter - widthBefore) / 2)
    leftUperXAfter = round((leftUperXBefore - leftUperXOffeset) if (leftUperXBefore - leftUperXOffeset) > 0 else 4)
    leftUperYAfter = round((leftUperYBefore - leftUperYOffeset) if (leftUperYBefore - leftUperYOffeset) > 0 else 4)
    rightDownXAfter = round(
        (rightDownXBefore + leftUperXOffeset) if (rightDownXBefore + leftUperXOffeset) < imgOriginWidth else (
                    imgOriginWidth - 4))
    rightDownYAfter = round(
        (rightDownYBefore + leftUperYOffeset) if (rightDownYBefore + leftUperYOffeset) < imgOriginHeight else (
                    imgOriginHeight - 4))
    imgOutput = imgInput[leftUperYAfter:rightDownYAfter, leftUperXAfter:rightDownXAfter]
    return imgOutput


def unzip_file(zip_src, dst_dir):
    r = zipfile.is_zipfile(zip_src)
    if r:
        fz = zipfile.ZipFile(zip_src, "r")
        for file in fz.namelist():
            fz.extract(file, dst_dir)
    else:
        return "please upload zipfiles again"


def fileDetermineType(filePathName):
    imgTypeList = {'jpg', 'bmp', 'png', 'jpeg', 'rgb', 'tif'}
    fileExtension = filePathName.rsplit('.', 1)[1]
    fileExtension = fileExtension.lower()
    if fileExtension in imgTypeList:
        return 1
    elif fileExtension == "zip":
        return 2
    else:
        return 0


if __name__ == "__main__":
    app.run(host='0.0.0.0',
            port=5000,
            debug=True
            )
